<template>
  <div class="m-snakerflow-designer" ref="lfElRef"></div>
  <PropertSetting
    ref="propertSettingRef"
    :extendAttrConfig="props.extendAttrConfig"
    :extendPropertyKeys="extendPropertyKeys"
    @change="handlePropertyChange"
  >
    <template #[key]="data" v-for="(item, key) in $slots">
      <slot :name="key" v-bind="data || {}"></slot>
    </template>
  </PropertSetting>
  <DataDetail ref="dataDetailRef" />
  <ImportData ref="importDataRef" @on-submit="handleImportSubmit" />
  <HighLightData ref="highLightDataRef" @on-submit="handleHighLightSubmit" />
</template>
<script lang="ts" setup>
  import LogicFlow from '@logicflow/core';
  import { Snapshot, DndPanel, SelectionSelect, Menu, Control, Group } from '@logicflow/extension';
  import '@logicflow/core/dist/style/index.css';
  import '@logicflow/extension/lib/style/index.css';
  import { onMounted, Ref, ref, unref, defineProps, watch, defineExpose, defineEmits } from 'vue';
  import { ProcessModel, PropertyEvent } from './types';
  import PropertSetting from './PropertySetting';
  import { SnakerFlowElement, SnakerFlowAdapter } from './snakerflow/index';
  import { NodeTypeEnum } from './enums';
  import DataDetail from './Control/DataDetail.vue';
  import ImportData from './Control/ImportData.vue';
  import HighLightData from './Control/HighLightData.vue';
  import { snakerXml2LogicFlowJson } from './snakerflow/tool';
  const emits = defineEmits(['update:value', 'on-save']);

  // 定义挂载元素Ref
  const lfElRef: Ref = ref(null);
  // 属性面板操作
  const propertSettingRef: Ref = ref(null);
  // 查看数据弹窗
  const dataDetailRef: Ref = ref(null);
  // 导入数据弹窗
  const importDataRef: Ref = ref(null);
  // 导入高高弹窗
  const highLightDataRef: Ref = ref(null);
  // 定义LogicFlow实例
  const lfInstance = ref(null) as Ref<LogicFlow | null>;
  // 当前操作节点/或边id
  const currentOpId = ref('');
  // 定义组件接收的参数
  const props = defineProps({
    value: {
      type: [Object, String],
    },
    config: {
      type: Object,
      default() {
        return {
          grid: true,
        };
      },
    },
    highLight: {
      // 高亮数据
      type: Object,
      default() {
        return {};
      },
    },
    viewer: {
      // 预览模式
      type: Boolean,
      default: false,
    },
    extendAttrConfig: {
      // 扩展属性配置
      type: Object,
    },
    showDoc: {
      // 是否显示文档
      type: Boolean,
      default: true,
    },
    // 扩展的属性key
    extendPropertyKeys: {
      type: Array,
    },
  });

  // 监听流程变化
  watch(
    () => props.value,
    () => {
      reRender(props.value);
    },
    {
      deep: true,
    }
  );
  // 监听高亮数据
  watch(
    () => props.highLight,
    () => {
      setHighLight(props.highLight);
    },
    {
      deep: true,
    }
  );
  const initDoc = () => {
    const lf = unref(lfInstance);
    if (!lf) return;
    if (props.showDoc) {
      // 控制面板-码云
      lf.extension.control.addItem({
        iconClass: 'lf-control-mayu',
        title: '',
        text: '码云',
        onClick: () => {
          window.location.href = 'https://gitee.com/mldong/snakerflow-designer-vue/tree/vue3/';
        },
      });
    }
  };
  // 初始化
  const init = () => {
    // 画布配置
    LogicFlow.use(Snapshot);
    LogicFlow.use(DndPanel);
    LogicFlow.use(SelectionSelect);
    LogicFlow.use(Menu);
    LogicFlow.use(Control);
    LogicFlow.use(SnakerFlowElement);
    LogicFlow.use(SnakerFlowAdapter);
    LogicFlow.use(Group);
    const defaultConfig: any = {};
    if (props.viewer) {
      // defaultConfig.isSilentMode = true
      defaultConfig.adjustEdge = false;
      defaultConfig.adjustNodePosition = false;
      defaultConfig.stopScrollGraph = true;
      defaultConfig.stopMoveGraph = true;
      defaultConfig.hoverOutline = false;
      defaultConfig.hideAnchors = true;
    }
    lfInstance.value = new LogicFlow({
      container: unref(lfElRef),
      grid: true,
      ...props.config,
      ...defaultConfig,
    });
    // 初始化操作
    initOp();
    reRender(props.value as ProcessModel);
    // 初始化事件
    initEvent();
  };
  // 初始化操作
  const initOp = () => {
    const lf = unref(lfInstance);
    if (!lf) return;
    if (props.viewer) {
      // 预览模式时
      lf.extension.menu.setMenuConfig({
        nodeMenu: [],
        edgeMenu: [],
      });
      // 删除上一步
      lf.extension.control.removeItem('undo');
      // 删除下一步
      lf.extension.control.removeItem('redo');
      initDoc();
      return;
    }
    // 设置右键菜单
    lf.extension.menu.setMenuConfig({
      nodeMenu: [
        {
          text: '删除',
          callback(node: any) {
            // node为该节点数据
            lf.deleteNode(node.id);
          },
        },
      ],
    });
    // 控制面板-清空画布
    lf.extension.control.addItem({
      iconClass: 'lf-control-clear',
      title: 'clear',
      text: '清空',
      onClick: () => {
        lf.clearData();
      },
    });
    // 控制面板-添加查看按钮
    lf.extension.control.addItem({
      iconClass: 'lf-control-see',
      title: '',
      text: '查看',
      onClick: () => {
        const dataDetailElRef = unref(dataDetailRef);
        if (!dataDetailElRef) return;
        const graphData = getGraphData();
        dataDetailElRef.show(graphData);
      },
    });
    // 控制面板-添加导入按钮
    lf.extension.control.addItem({
      iconClass: 'lf-control-import',
      title: '',
      text: '导入',
      onClick: () => {
        unref(importDataRef).show();
      },
    });
    // 控制面板-设置高亮数据
    lf.extension.control.addItem({
      iconClass: 'lf-control-setting',
      title: '',
      text: '设置高亮',
      onClick: () => {
        unref(highLightDataRef).show();
      },
    });
    // 控制面板-保存
    lf.extension.control.addItem({
      iconClass: 'lf-control-save',
      title: '',
      text: '保存',
      onClick: () => {
        emits('on-save', getGraphData());
      },
    });
    initDoc();
    // 设置默认边
    lf.setDefaultEdgeType('wlf:transition');
    // 设置拖拽面板
    lf.extension.dndPanel.setPatternItems([
      {
        type: 'wlf:start',
        text: '开始',
        label: '开始节点',
        properties: {},
        icon: '',
      },
      {
        type: 'wlf:task',
        text: '用户任务',
        label: '用户任务',
        properties: {},
        icon: '',
        className: 'important-node',
      },
      {
        type: 'wlf:custom',
        text: '自定义任务',
        label: '自定义任务',
        properties: {},
        icon: '',
        className: 'import_icon',
      },
      {
        type: 'wlf:decision',
        label: '条件判断',
        properties: {},
        icon: '',
      },
      {
        type: 'wlf:fork',
        label: '分支',
        properties: {},
        icon: '',
      },
      {
        type: 'wlf:join',
        label: '合并',
        properties: {},
        icon: '',
      },
      {
        type: 'wlf:end',
        text: '结束',
        label: '结束节点',
        properties: {},
        icon: '',
      },
      {
        type: 'wlf:wfSubProcess',
        text: 'snaker子流程',
        label: 'snaker子流程',
        properties: {},
        icon: '',
        className: 'import_icon',
      },
      {
        type: 'wlf:subProcess',
        text: '',
        label: '子流程',
        properties: {},
        icon: '',
        className: 'import_icon',
      },
    ]);
  };
  // 初始化事件
  const initEvent = () => {
    if (props.viewer) return;
    // 初始化事件
    const lf = unref(lfInstance);
    if (!lf) return;
    // 空白区右键事件-弹出流程属性表单
    lf.graphModel.eventCenter.on('blank:contextmenu', () => {
      propertSettingRef.value.show({
        name: lf.graphModel.name,
        displayName: lf.graphModel.displayName,
        expireTime: lf.graphModel.expireTime,
        instanceUrl: lf.graphModel.instanceUrl,
        instanceNoClass: lf.graphModel.instanceNoClass,
        preInterceptors: lf.graphModel.preInterceptors,
        postInterceptors: lf.graphModel.postInterceptors,
        type: 'process',
      });
    });
    // 节点点击事件
    lf.graphModel.eventCenter.on('node:click', (args) => {
      if (args.data.type === 'wlf:subProcess') {
        // 子流程展开折叠时，不处理
        if (!args.e.srcElement.className.baseVal) {
          return;
        }
      }
      currentOpId.value = args.data.id;
      propertSettingRef.value.show({
        ...args.data.properties,
        name: args.data.id,
        displayName: args.data.text?.value || args.data.properties.displayName,
        type: args.data.type,
      });
    });
    // 边点击事件
    lf.graphModel.eventCenter.on('edge:click', (args) => {
      currentOpId.value = args.data.id;
      propertSettingRef.value.show({
        ...args.data.properties,
        name: args.data.id,
        displayName: args.data.text?.value,
        type: args.data.type,
      });
    });
  };
  // 重新渲染
  const reRender = (data: any): void => {
    const lf = unref(lfInstance);
    if (!lf) return;
    lf.render(data);
    // 设置高亮
    setHighLight(props.highLight);
  };
  // 处理属性值变化事件
  const handlePropertyChange = (e: PropertyEvent) => {
    const lf = unref(lfInstance);
    if (!lf) return;
    if (e.type === NodeTypeEnum.process) {
      // 流程属性
      lf.graphModel[e.propertyName] = e.propertyValue;
    } else if (e.type === NodeTypeEnum.subProcess) {
      // 子流程属性
      const nodeId = unref(currentOpId);
      if (e.propertyName === 'name') {
        // 更新唯一标识
        if (!lf.getNodeModelById(e.propertyValue)) {
          lf.changeNodeId(nodeId, e.propertyValue);
          currentOpId.value = e.propertyValue;
        }
      } else {
        lf.setProperties(nodeId, {
          [e.propertyName]: e.propertyValue,
        });
      }
    } else if (
      (
        [
          NodeTypeEnum.custom,
          NodeTypeEnum.decision,
          NodeTypeEnum.end,
          NodeTypeEnum.fork,
          NodeTypeEnum.join,
          NodeTypeEnum.start,
          NodeTypeEnum.task,
          NodeTypeEnum.wfSubProcess,
        ] as NodeTypeEnum[]
      ).includes(e.type)
    ) {
      // 节点属性
      const nodeId = unref(currentOpId);
      // 节点信息
      if (e.propertyName === 'name') {
        // 更新唯一标识
        if (!lf.getNodeModelById(e.propertyValue)) {
          lf.changeNodeId(nodeId, e.propertyValue);
          currentOpId.value = e.propertyValue;
        }
      } else if (e.propertyName === 'displayName') {
        // 更新节点文本值
        lf.updateText(nodeId, e.propertyValue);
      } else if (e.propertyName === 'width' && [NodeTypeEnum.task, NodeTypeEnum.custom].includes(e.type)) {
        // 宽度
        lf.setProperties(nodeId, {
          width: (Number.isNaN(e.propertyValue) ? 120 : e.propertyValue) as number,
        });
        const nodeModel = lf.getNodeModelById(nodeId);
        if (nodeModel) {
          nodeModel.width = (Number.isNaN(e.propertyValue) ? 120 : e.propertyValue) as number;
        }
      } else if (e.propertyName === 'height' && [NodeTypeEnum.task, NodeTypeEnum.custom].includes(e.type)) {
        // 高度
        lf.setProperties(nodeId, {
          height: (Number.isNaN(e.propertyValue) ? 120 : e.propertyValue) as number,
        });
        const nodeModel = lf.getNodeModelById(nodeId);
        if (nodeModel) {
          nodeModel.height = (Number.isNaN(e.propertyValue) ? 120 : e.propertyValue) as number;
        }
      } else if (e.propertyName === 'field') {
        // 更新扩展属性
        lf.setProperties(nodeId, {
          field: JSON.stringify(e.propertyValue),
        });
      } else {
        // 更新基础属性
        lf.setProperties(nodeId, {
          [e.propertyName]: e.propertyValue,
        });
      }
      emits('update:value', getGraphData());
    } else if (e.type === NodeTypeEnum.transition) {
      // 边属性
      const edgeId = unref(currentOpId);
      if (e.propertyName === 'name') {
        // 更新唯一标识
        if (!lf.getEdgeModelById(e.propertyValue)) {
          lf.changeEdgeId(edgeId, e.propertyValue);
          currentOpId.value = e.propertyValue;
        }
      } else if (e.propertyName === 'displayName') {
        // 更新节点文本值
        lf.updateText(edgeId, e.propertyValue);
      } else {
        // 更新基础属性
        lf.setProperties(edgeId, {
          [e.propertyName]: e.propertyValue,
        });
      }
    }
  };
  /**
   * 获取流程数据
   */
  const getGraphData = () => {
    const lf = unref(lfInstance);
    if (!lf) return {};
    return lf.getGraphData();
  };
  /**
   * 处理导入提交事件
   */
  const handleImportSubmit = (str: string) => {
    let data: any = null;
    try {
      data = JSON.parse(str);
    } catch {
      try {
        data = snakerXml2LogicFlowJson(str);
      } catch {
        return;
      }
    }
    if (data && data.nodes && data.edges) {
      reRender(data);
    }
  };
  // 导入json
  const importJson = (data: any) => {
    reRender(data);
  };
  // 导入xml
  const importXml = (xml: any) => {
    reRender(xml);
  };
  // 刷新导入下拉数据
  const refreshImport = () => {
    return unref(importDataRef)?.refresh();
  };
  // 导入高亮数据
  const handleHighLightSubmit = (jsonStr: string) => {
    let data: any = null;
    try {
      data = JSON.parse(jsonStr);
    } catch {}
    setHighLight(data);
  };
  /**
   * 设置高亮数据
   * @param data { "historyNodeNames": [], "historyEdgeNames": [], "activeNodeNames": []}
   */
  const setHighLight = (data: any) => {
    const lf = unref(lfInstance);
    if (!lf) return;
    // 设置历史节点
    if (data && data.historyNodeNames) {
      data.historyNodeNames.forEach((nodeName: string) => {
        lf.setProperties(nodeName, { state: 'history' });
      });
    }
    // 设置当前节点
    if (data && data.activeNodeNames) {
      data.activeNodeNames.forEach((nodeName: string) => {
        lf.setProperties(nodeName, { state: 'active' });
      });
    }
    // 设置历史边
    if (data && data.historyEdgeNames) {
      data.historyEdgeNames.forEach((edgeName: string) => {
        const edgeModel = lf.getEdgeModelById(edgeName);
        if (edgeModel) {
          edgeModel.setProperties({ state: 'history' });
        }
      });
    }
  };
  onMounted(init);
  // 导出提供给外部操作-$refs.xxx
  defineExpose({
    getGraphData,
    refreshImport,
    importJson,
    importXml,
    setHighLight,
  });
</script>
<style scoped>
  .m-snakerflow-designer {
    width: 100%;
    height: 100%;
    position: relative;
  }
</style>
<style>
  .lf-control-see {
    background-image: url('');
  }
  .lf-control-import {
    background-image: url('');
  }
  .lf-control-clear {
    background-image: url('');
  }
  .lf-control-save {
    background-image: url('');
  }
  .lf-control-mayu {
    background-image: url('');
  }
  .lf-control-join {
    width: 70px;
    height: 55px;
    background-repeat: no-repeat;
    background-size: 35px 30px;
    background-position: center;
    background-image: url('');
  }
  .lf-control-fork {
    width: 70px;
    height: 55px;
    background-repeat: no-repeat;
    background-size: 35px 30px;
    background-position: center;
    background-image: url('');
  }
  .lf-control-setting {
    width: 70px;
    height: 55px;
    background-repeat: no-repeat;
    background-size: 35px 30px;
    background-position: center;
    background-image: url('');
  }
</style>
